package org.javaopenaccess.consoleapp;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import org.javaopenaccess.model.ClassMetadata;
import org.javaopenaccess.model.InternalSession;
import org.javaopenaccess.model.Schema;
import org.javaopenaccess.model.User;
import org.javaopenaccess.service.classes.DynamicClassService;
import org.javaopenaccess.service.schema.SchemaService;
import org.javaopenaccess.service.session.SessionService;
import org.javaopenaccess.service.user.UserNotFoundException;
import org.javaopenaccess.service.user.UserService;
import org.springframework.beans.factory.InitializingBean;

public class ConsoleMenuController implements InitializingBean{

	private static final String AUTOLOGIN_NAME = "Chewbacca";
	private SessionService sessionService;
	private SchemaService schemaService;
	private UserService userService;
	private InternalSession session;
	private DynamicClassService dynamicClassService;
	private Class<?> currentClass;

	private User autoLoginUser;
	
	@Override
	public void afterPropertiesSet() throws Exception {
		try {
			autoLoginUser = userService.getUser(AUTOLOGIN_NAME);
			print("Using user:"+autoLoginUser.getUserName());
		} catch (UserNotFoundException e) {
			print("Error! Cannot autologin. No user found. Creating user...");
			autoLoginUser = new User(AUTOLOGIN_NAME);
			userService.createUser(autoLoginUser);
		}
		
	}
	protected void print(String... data) {
		for (String string : data) {
			System.out.println(string);
		}
	}

	private void handleChoice() {
		Scanner reader = new Scanner(System.in);
		try {
			int choice = reader.nextInt();
			switch (choice) {
			case 0:
				System.exit(0);
				break;
			case 1:
				Schema schema = schemaService.createSchema(queryForString("Please enter schema name:"), autoLoginUser); 
				session = sessionService.createSession(schema);
				print("automatically started a new session with user:" + autoLoginUser.getUserName(),
						"and schema:"+schema.getName());
				break;
			case 2:
				schema = schemaService.getSchema(queryForString("Please enter schema name:"));
				session = sessionService.createSession(schema);
				print("started a new session with user:" + autoLoginUser.getUserName(),
						"and schema:"+schema.getName());
				break;
			case 3:
				ClassMetadata meta =  new ClassMetadata(queryForString("Please enter class name:"));
				Map<String, String> attrs = new HashMap<String, String>();
				String attName;
				String attType;
				boolean cont = true;
				do{
					attName = queryForString("Please enter attribute name (:q to end inputing attributes):");
					if (attName.equals(":q")){
						cont = false;
						break;
					}
					attType = queryForString("Please enter attribute type (eg. java.lang.String):");
					if (attName != null && !attName.isEmpty() && attType != null && !attType.isEmpty()){
						attrs.put(attName, attType);
					}
				}while (cont);
							
				meta.setAttributes(attrs);
				Class<?> clazz = dynamicClassService.createType(meta, session);
				printClass(clazz);
				currentClass = clazz;
				break;
			case 4:
				print("Insert data for class:"+currentClass.getName());
				Map<String, Object> values = new HashMap<String, Object>();
				for (Field field: currentClass.getDeclaredFields()){
					String data = queryForString("Please enter value for field: "+field.getName() +
							"(Type:"+field.getType().getName()+")");
					Object value = field.getType().cast(data);
					values.put(field.getName(), value);
				}
				dynamicClassService.createTypeInstance(currentClass, session, values);
				break;
			default:
				throw new IllegalArgumentException();
			}
		}catch (IllegalArgumentException e){
			print("An error occurred. Please contact your sysadmin or dba.");
		} catch (Exception e) {
			print("Illegal selection, please make a valid selection");
			e.printStackTrace();
			
		}finally{
			displayMenu();
		}
	}


	private String queryForString(String question) {
		print(question);
		Scanner reader = new Scanner(System.in);
		try {
			String name = reader.nextLine();
			if (name.length() > 255 || name.isEmpty())
				throw new IllegalArgumentException();
			return name;
		} catch (Exception e) {
			System.out.println("Illegal name. Please enter again");
			reader.close();
			return queryForString(question);
		}
	}

	public void displayMenu() {
		if (session != null)
			print("Session start time "+new Date(session.getSessionStartTime()));
		// These should be localized
		print("","1. Create a database schema and start a user session");
		print("2. Load a database schema and start a user session");
		if (session != null){
			print("3. Create type");
		}
		if (currentClass != null){
			print("4. Create type instance");
		}
		print("0. Quit");
		handleChoice();
	}
	
	private void printClass(Class<?> clazz){
		int width = clazz.getName().length();
		printClassSeparator(width);
		print ("","Class Name:",clazz.getName());
		printClassSeparator(width);	
		print("","Attributes:");
		for (Field field : clazz.getDeclaredFields()) {
			print ("-"+field.getName() + "  :"+field.getType().getName());
		}
		printClassSeparator(width);
		print("");
		print("Methods:");
		for (Method method : clazz.getDeclaredMethods()) {
			print ("+"+method.getName());
		}
		printClassSeparator(width);
		print("");
		
	}
	private void printClassSeparator(int width) {
		for (int i = 0; i < width + 5; i++) {
			System.out.print("-");
		}
	}

	public void setSessionService(SessionService sessionService) {
		this.sessionService = sessionService;
	}
	public void setSchemaService(SchemaService schemaService) {
		this.schemaService = schemaService;
	}
	public void setInternaSessionCreationService(SessionService sessionService) {
		this.sessionService = sessionService;
	}
	public void setUserService(UserService userService) {
		this.userService = userService;
	}
	public void setDynamicClassService(DynamicClassService dynamicClassService) {
		this.dynamicClassService = dynamicClassService;
	}
}
